package cn.kzgg.ymcc.service.impl;

import cn.kzgg.ymcc.doc.CourseDoc;
import cn.kzgg.ymcc.dto.CourseSearchDto;
import cn.kzgg.ymcc.esmapper.HighlightResultMapper;
import cn.kzgg.ymcc.repository.CourseEsRepository;
import cn.kzgg.ymcc.result.PageList;
import cn.kzgg.ymcc.service.ICourseEsService;
import cn.kzgg.ymcc.vo.AggrPageList;
import cn.kzgg.ymcc.vo.AggrsBucket;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class CourseEsServiceImpl implements ICourseEsService {

    @Autowired
    private CourseEsRepository courseEsRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    private HighlightResultMapper highlightResultMapper;
    /**
     * 课程上架
     * @param courseDoc
     */
    @Override
    public void saveCourse(CourseDoc courseDoc) {
        courseEsRepository.save(courseDoc);
    }

    /**
     * 下架
     * @param courseId
     */
    @Override
    public void remCourse(Long courseId) {
        courseEsRepository.deleteById(courseId);
    }

    /**
     * 课程列表  Es分页查询
     * @param courseSearchDto
     * @return
     */
    @Override
    public PageList<CourseDoc> search(CourseSearchDto courseSearchDto) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        //封装排序条件========================
        if(StringUtils.hasLength(courseSearchDto.getSortField())){
            // dto.getSortField()  xl rq  xp pl
            String sortField = null;
            switch (courseSearchDto.getSortField().toLowerCase()){
                case "xl": sortField="saleCount";break;
                case "xp": sortField="onlineTime";break;
                case "pl": sortField="commentCount";break;
                case "jg": sortField="price";break;
                case "rq": sortField="viewCount";break;
                default: break;
            }
            if(sortField != null){
                if(StringUtils.hasLength(courseSearchDto.getSortType())){
                    SortOrder sortOrder = courseSearchDto.getSortType().equalsIgnoreCase("ASC") ? SortOrder.ASC : SortOrder.DESC;
                    builder.withSort(new FieldSortBuilder(sortField).order(sortOrder));
                }
            }
        }
        //封装分页相关的参数===================  0 代表第一页
        builder.withPageable(PageRequest.of(courseSearchDto.getPage()-1, courseSearchDto.getRows()));
        //PageRequest pageRequest = PageRequest.of(dto.getPage()-1, dto.getRows());
        //封装查询条件========================
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //DSL查询  private String keyword;//关键字-------
        if(StringUtils.hasLength(courseSearchDto.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("name",courseSearchDto.getKeyword()));
        }
        //DSL过滤-----------------
        if(courseSearchDto.getCourseTypeId() != null){
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId",courseSearchDto.getCourseTypeId()));
        }
        if(StringUtils.hasLength(courseSearchDto.getGradeName())){
            boolQuery.filter(QueryBuilders.termQuery("gradeName",courseSearchDto.getGradeName()));
        }
        if(StringUtils.hasLength(courseSearchDto.getChargeName())){
            boolQuery.filter(QueryBuilders.termQuery("chargeName",courseSearchDto.getChargeName()));
        }
        if(courseSearchDto.getPriceMin() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(courseSearchDto.getPriceMin()));
        }
        if(courseSearchDto.getPriceMax() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(courseSearchDto.getPriceMax()));
        }

        /**
         * 实现高亮：
         * 1.指定哪个字段需要高亮，设置高亮样式
         * 2.处理mapper映射结果，准备结果映射器，拷贝mapper映射文件
         * 3.使用映射处理结果，替换原来的courseEsRepository为ElasticSearchRestTemplate，使用queryForPage跟上mapper映射实例
         */
        builder.withHighlightBuilder(
                new HighlightBuilder().field("name").preTags("<font style='color:red'>").postTags("</font>"));

        //聚合：添加聚合查询条件=========================
        //根据等级名字聚合
        builder.addAggregation(AggregationBuilders.terms("aggGradeName").field("gradeName"));
        //根据收费方式聚合
        builder.addAggregation(AggregationBuilders.terms("aggChargeName").field("chargeName"));

        builder.withQuery(boolQuery);
        //搜索Es  ==========================
        NativeSearchQuery build = builder.build();


        //Page<CourseDoc> page = courseEsRepository.search(build);//  ##########
        //实现高亮,替换courseEsRepository为ElasticSearchRestTemplate
        //Page<CourseDoc> page = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);

        //处理聚合结果============================
        //1.返回值使用Page的子类AggregatedPage
        AggregatedPage<CourseDoc> page = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);
        //2.分别获取不同聚合结果,处理集合结果用map接收
        Map<String, Aggregation> aggregationMap = page.getAggregations().asMap();
        //将查询的条件用terms聚合结果json序列化处理,因为要返回给前端
        ParsedStringTerms aggGradeName = (ParsedStringTerms) aggregationMap.get("aggGradeName");
        ParsedStringTerms aggChargeName = (ParsedStringTerms) aggregationMap.get("aggChargeName");
        //3.遍历两个集合结果返回list
        //遍历是否支付结果集
        List<AggrsBucket> aggrsBuckets = new ArrayList<>();
        aggChargeName.getBuckets().forEach(bucket->{
            String key = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            AggrsBucket aggrsBucket = new AggrsBucket(key,docCount);
            aggrsBuckets.add(aggrsBucket);
        });
        List<AggrsBucket> aggrsBucketss = new ArrayList<>();
        aggGradeName.getBuckets().forEach(bucket->{
            String key = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            AggrsBucket aggrsBucket = new AggrsBucket(key,docCount);
            aggrsBucketss.add(aggrsBucket);
        });
        //4.将结果存到map
        Map<String, List<AggrsBucket>> map = new HashMap<>();
        map.put("aggGradeName",aggrsBucketss);
        map.put("aggChargeName",aggrsBuckets);

        //封装PageList返回 ==================
        //5.用AggrPageList接收
        return new AggrPageList<>(page.getTotalElements(),page.getContent(),map);
    }

}
